package gate;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.google.protobuf.Message;
import gate.client.ClientConnection;
import gate.client.ClientConnectionMap;
import gate.handler.GateLogicConnectionHandler;
import io.netty.buffer.ByteBuf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import protobuf.Utils;
import protobuf.generate.cli2srv.login.Login;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;

/**
 * 处理所有消息的信使
 * Created by tianfei on 2017/4/17.
 */
public class ClientMessager {
    private static final Logger logger = LoggerFactory.getLogger(ClientMessager.class);
    private static final String SERVER_SECRET = "Hjs#n)nQ%3d*5(mm@ef&ds";

    public static HashMap<Integer, Transfer> tranferHandlerMap = new HashMap<>();
    public static HashMap<Class<?>, Integer> msg2ptoNum = new HashMap<>();

    static Algorithm algorithm = null;
    static JWTVerifier verifier = null;
    static {

        //初始化验证token的verifier
        try {
            algorithm = Algorithm.HMAC256(SERVER_SECRET);
            verifier = JWT.require(algorithm)
                    .withIssuer("tianff.net")
                    .build(); //Reusable verifier instance
        } catch (UnsupportedEncodingException e) {
            logger.error("JWT algorithm & verifier initialize failed!..........");
            e.printStackTrace();
        }
    }

    @FunctionalInterface
    public interface Transfer{
        void process(Message msg, ClientConnection conn) throws IOException;
    }

    public static void registerTranferHandler(Integer ptoNum, Transfer tranfer, Class<?> cla) {
        if (tranferHandlerMap.get(ptoNum) == null)
            tranferHandlerMap.put(ptoNum, tranfer);
        else {
            logger.error("pto has been registered in transeerHandlerMap, ptoNum: {}", ptoNum);
            return;
        }

        if(msg2ptoNum.get(cla) == null)
            msg2ptoNum.put(cla, ptoNum);
        else {
            logger.error("pto has been registered in msg2ptoNum, ptoNum: {}", ptoNum);
            return;
        }
    }

    public static void processTransferHandler(Message msg, ClientConnection conn) throws IOException {
        logger.info("MessageName {}", msg.getClass());
        int ptoNum = msg2ptoNum.get(msg.getClass());
        Transfer transferHandler = tranferHandlerMap.get(ptoNum);

        if(transferHandler != null) {
            transferHandler.process(msg, conn);
        }
    }

    //fixme 暂时不做聊天
    public static void transfer2Logic(Message msg, ClientConnection conn) {
        ByteBuf byteBuf = null;
        if(conn.getUserId() == null ) {
            logger.error("User not login.");
            return;
        }

//        if(msg instanceof Chat.CPrivateChat) {
//            byteBuf = Utils.pack2Server(msg, ParseMap.getPtoNum(msg), conn.getNetId(), Internal.Dest.Logic, conn.getUserId());
//        }

        GateLogicConnectionHandler.getGatelogicConnection().writeAndFlush(byteBuf);
    }

    //fixme 暂时不传给Auth服务器，gate自己鉴权
    public static void transfer2Auth(Message msg, ClientConnection conn) {

//        ByteBuf byteBuf = null;
//        if(msg instanceof Login.CLogin) {
//            String token = ((Login.CLogin) msg).getToken();
//
//            String userID = getUserIDFromToken(token);
//
//            byteBuf = Utils.pack2Server(msg, ParseMap.getPtoNum(msg), conn.getNetId(), Internal.Dest.Auth);
//            ClientConnectionMap.registerUserid(token, conn.getNetId());
//        }
////        else if(msg instanceof Auth.CRegister) {
////            byteBuf = Utils.pack2Server(msg, ParseMap.getPtoNum(msg), conn.getNetId(), Internal.Dest.Auth, ((Auth.CRegister) msg).getUserid());
////        }
//
//        GateAuthConnectionHandler.getGateAuthConnection().writeAndFlush(byteBuf);

        //模拟Auth服务器验证
        mookAuthServerResponse(msg,conn);
    }

    //把一条消息推送给所有的用户
    public static void pushMsgToClient(Message msg){
        ByteBuf out = Utils.pack2Client(msg);
        for (Long netid: ClientConnectionMap.allClientMap.keySet()){
            ClientConnectionMap.allClientMap.get(netid).getCtx().writeAndFlush(out);
        }
    }


    public static void mookAuthServerResponse(Message msg,ClientConnection conn){

        try {

            if (!(msg instanceof Login.CLogin)){
                return;
            }

            String token =((Login.CLogin) msg).getToken();

            DecodedJWT jwt = verifier.verify(token);

            if (jwt.getExpiresAt().before(new Date(System.currentTimeMillis()))){

                //token 超时

                //创建返回信息
                Message overTimeMsg = Login.SResponse.newBuilder()
                        .setCode(405)
                        .setDesc("token over time!")
                        .build();
                ByteBuf ret=Utils.pack2Client(overTimeMsg);

                //返回消息
                conn.getCtx().writeAndFlush(ret);

                //断开与当前用户的连接
                conn.getCtx().close();

                return;
            }

            String uid=jwt.getClaim("uid").asInt()+"";
            logger.info("jwt.claim:uid: "+uid);

            conn.setUserId(uid);
            //通过验证，注册用户到用户Map
            //ClientConnectionMap.addClientConnection(conn.getCtx());
            ClientConnectionMap.registerUserid(uid,conn.getNetId());


            //返回客户端连接成功的信息
            ByteBuf ret=Utils.pack2Client(Login.SResponse.newBuilder()
                    .setCode(200)
                    .setDesc("client "+uid+" connected.")
                    .build());
            conn.getCtx().writeAndFlush(ret);


        } catch (final JWTVerificationException e) {
            //token错误

            //创建返回信息
            Message overTimeMsg = Login.SResponse.newBuilder()
                    .setCode(403)
                    .setDesc("token invalid!")
                    .build();
            ByteBuf ret=Utils.pack2Client(overTimeMsg);

            //返回消息
            conn.getCtx().writeAndFlush(ret);
            //清空连接
            ClientConnectionMap.removeClientConnection(conn.getCtx());

            //断开与当前用户的连接
            conn.getCtx().close();
        }


    }

}
